/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "kh_sle_host.h"
#include "sle_loader_hal.h"
#include "sle_host_hal.h"
#include "sle_hilog.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <cmsis_os2.h>
#include "sle_lite_advance_setting.h"

static bool g_isSleHostServiceInit = false;
static SleDeviceState g_sleHostState = SLE_HOST_STATE_ERROR;
static SleDeviceAddress g_hostAddr = {SLE_PUBLIC_ADDRESS_TYPE, {0}};
static uint8_t g_hostName[SLE_DEVICE_NAME_LEN] = {SLE_HOST_NAME_PRE_FIX};

// TODO: 后续serviceCallbackNum 通过sysParam获取
#ifndef ServiceCallbackNumFlag
#define ServiceCallbackNumFlag
#define serviceCallbackNum  3
#endif  // ServiceCallbackNumFlag
static SleHostCallbacks* g_hostCallbacks[serviceCallbackNum] = {NULL};
static SleHostHalCallbacks *g_halSleHostCallback = NULL;
static osMutexId_t g_hostCallbcakMuxHandle = 0;
static osSemaphoreId_t g_sleHostSemHandle = 0;
static uint32_t SleHostTimeOut = 300; //ticks
static SleErrorCode g_sleHostAsyncRet = SLE_ERR_UNKNOWN;

void NotifySleHostStateChange(SleDeviceState hostState)
{
    SLE_LOGD("NotifySleHostStateChange In(%d)", hostState);
    osMutexAcquire(g_hostCallbcakMuxHandle, 0);
    g_sleHostState = hostState;
    for (uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_hostCallbacks[i] != NULL && g_hostCallbacks[i]->OnSleHostStateChangeCb != NULL) {
            g_hostCallbacks[i]->OnSleHostStateChangeCb(g_sleHostState);
        }
    }
    osMutexRelease(g_hostCallbcakMuxHandle);
}

void OnSleHalEnableCallback(SleHalErrorCode errCode)
{
    SLE_LOGD("OnSleHalEnableCallback In errCode(%d)", errCode);
    g_sleHostAsyncRet = (SleErrorCode)errCode;
    osSemaphoreRelease(g_sleHostSemHandle);
    if(g_sleHostAsyncRet == SLE_NO_ERROR) {
        NotifySleHostStateChange(SLE_HOST_STATE_ENABLED);
    } else {
        NotifySleHostStateChange(SLE_HOST_STATE_DISABLED);
    }
}

void OnSleHalDisableCallback(SleHalErrorCode errCode)
{
    SLE_LOGD("OnSleHalDisableCallback  errCode(%d)", errCode);
    g_sleHostAsyncRet = (SleErrorCode)errCode;
    osSemaphoreRelease(g_sleHostSemHandle);
    if(g_sleHostAsyncRet == SLE_NO_ERROR) {
        NotifySleHostStateChange(SLE_HOST_STATE_DISABLED);
    } else {
        NotifySleHostStateChange(SLE_HOST_STATE_ENABLED);
    }
}

SleErrorCode InitSleHostService(void)
{
    SLE_LOGD("InitSleHostService In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == false, SLE_NO_ERROR);
    SleErrorCode ret = SLE_ERR_FAILED;
    //初始化hal
    ret = (SleErrorCode) InitSleHal();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    ret = (SleErrorCode) InitSleHostHal();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    //框架保存本机地址与设备名
    ret = (SleErrorCode) SleHostHalGetHostAddress(&g_hostAddr);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    SLE_LOGI("Local Sle address: %02x:%02x:%02x:%02x:%02x:%02x", \
        g_hostAddr.addr[0], g_hostAddr.addr[1], g_hostAddr.addr[2], g_hostAddr.addr[3], g_hostAddr.addr[4], g_hostAddr.addr[5]);
    sprintf(g_hostName + SLE_HOST_NAME_PRE_FIX_LEN, "%02X%02X", g_hostAddr.addr[4], g_hostAddr.addr[5]);
    ret = (SleErrorCode) SleHostHalSetHostName(g_hostName, strlen(g_hostName));
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    //框架注册回调
    if (g_halSleHostCallback == NULL) {
        g_halSleHostCallback = (SleHostHalCallbacks*)malloc(sizeof(SleHostHalCallbacks));
        SLE_ASSERT_AND_RETURN_RET(g_halSleHostCallback != NULL, SLE_ERR_NO_MEM);
        g_halSleHostCallback->OnHostHalEnableSleCb = OnSleHalEnableCallback;
        g_halSleHostCallback->OnHosthalDisableSleCb = OnSleHalDisableCallback;
    }
    ret = (SleErrorCode) SleHostHalRegisterCallback(g_halSleHostCallback);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    //全局变量初始化
    if (g_hostCallbcakMuxHandle == 0) {
        osMutexAttr_t attr = {
            .name = "sle_host_mux",
        };
        g_hostCallbcakMuxHandle = osMutexNew(&attr);
    }
    if(g_sleHostSemHandle == 0) {
        // osStatus_t uwRet = LOS_BinarySemCreate(1, &g_sleHostSemHandle);
        g_sleHostSemHandle = osSemaphoreNew(1, 0, NULL);
        // SLE_ASSERT_AND_RETURN_RET(uwRet == osOK, SLE_ERR_SYSTEM_WRONG);
    }
    g_sleHostAsyncRet = SLE_ERR_UNKNOWN;
    g_isSleHostServiceInit = true;
    g_sleHostState = SLE_HOST_STATE_DISABLED;
    return ret;
}

SleErrorCode DeinitSleHostService(void)
{
    SLE_LOGD("DeinitSleHostService In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SleErrorCode ret = SLE_ERR_FAILED;
    if (g_sleHostState != SLE_HOST_STATE_DISABLED) {
        SLE_LOGW("current host status is not disabled, disable it first");
        ret = SLE_ERR_STATUS_WRONG;
    }
    ret = (SleErrorCode) DeinitSleHostHal();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    SLE_ASSERT_AND_RETURN_RET(g_hostCallbcakMuxHandle != 0, SLE_ERR_STATUS_WRONG); 
    osMutexAcquire(g_hostCallbcakMuxHandle, 0);
    if(g_halSleHostCallback == NULL) {
        SLE_LOGW("g_halSleHostCallback is NULL");
    } else {
        free(g_halSleHostCallback);
        g_halSleHostCallback = NULL;
    }
    osMutexRelease(g_hostCallbcakMuxHandle);
    osMutexDelete(g_hostCallbcakMuxHandle);
    g_hostCallbcakMuxHandle = 0;  
    osSemaphoreDelete(g_sleHostSemHandle);
    g_sleHostSemHandle = 0;
    g_sleHostAsyncRet = SLE_ERR_UNKNOWN;
    g_isSleHostServiceInit = false;
    g_sleHostState = SLE_HOST_STATE_ERROR;
    return ret;
}

SleErrorCode CheckCanEnable(void)
{
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_DISABLED, SLE_NO_ERROR);
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_ENABLED, SLE_ERR_ENABLE_SUCCESS_WHEN_ENABLED);
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_ENABLING, SLE_ERR_ENABLE_FAILED_WHEN_ENABLING);
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_DISABLING, SLE_ERR_ENABLE_FAILED_WHEN_DISABLING);
    return SLE_ERR_UNKNOWN;
}

SleErrorCode SleEnable(void)
{
    SLE_LOGD("SleEnable In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SleErrorCode ret = CheckCanEnable();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    ret = (SleErrorCode) SleHostHalEnableSle();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    osStatus_t uwRet = osSemaphoreAcquire(g_sleHostSemHandle, SleHostTimeOut);
    SLE_ASSERT_AND_RETURN_RET(uwRet != osErrorTimeout, SLE_ERR_TIME_OUT);
    ret = g_sleHostAsyncRet;
    ret = (SleErrorCode) SleHostHalSetHostAddress(&g_hostAddr);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    ret = (SleErrorCode) SleHostHalSetHostName(g_hostName, strlen(g_hostName));
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return ret;
}

SleErrorCode CheckCanDisable(void)
{
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_DISABLED, SLE_ERR_DISABLE_SUCCESS_WHEN_DISABLED);
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_ENABLED, SLE_NO_ERROR);
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_ENABLING, SLE_ERR_DISABLE_FAILED_WHEN_ENABLING);
    SLE_ASSERT_AND_RETURN_RET(g_sleHostState != SLE_HOST_STATE_DISABLING, SLE_ERR_DISABLE_FAILED_WHEN_DISABLING);
    return SLE_ERR_UNKNOWN;
}

SleErrorCode SleDisable(void)
{
    SLE_LOGD("SleDisable In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SleErrorCode ret = CheckCanDisable();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    ret = (SleErrorCode) SleHostHalDisableSle();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    osStatus_t uwRet = osSemaphoreAcquire(g_sleHostSemHandle, SleHostTimeOut);
    SLE_ASSERT_AND_RETURN_RET(uwRet != osErrorTimeout, SLE_ERR_TIME_OUT);
    ret = g_sleHostAsyncRet;
    return ret;
}

SleErrorCode GetSleState(SleDeviceState* sleHostState)
{
    SLE_LOGD("GetSleState In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(sleHostState != NULL, SLE_ERR_INVALID_PARAM);
    *sleHostState = g_sleHostState;
    return SLE_NO_ERROR;
}

SleErrorCode GetHostAddress(SleDeviceAddress *hostAddr)
{
    SLE_LOGD("GetHostAddress In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(hostAddr != NULL, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(hostAddr->addr != NULL, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = SLE_ERR_FAILED;
    ret = SleHostHalGetHostAddress(hostAddr);
    SLE_ASSERT_AND_RETURN_RET((ret == SLE_NO_ERROR), SLE_ERR_DRIVER_OR_MODUAL_GOES_WRONG);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    if(hostAddr->addrType == SLE_PUBLIC_ADDRESS_TYPE) {
        memcpy(&g_hostAddr, hostAddr, sizeof(SleDeviceAddress));
    }
    return ret;
}

SleErrorCode SetHostName(const uint8_t *name, uint8_t len)
{
    SLE_LOGD("SetHostName In");
    SLE_ASSERT_AND_RETURN_RET_LOG(len != 0, SLE_ERR_INVALID_PARAM, "param name null error!");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SleErrorCode ret = SLE_ERR_FAILED;
    SLE_ASSERT_AND_RETURN_RET((len <= SLE_DEVICE_NAME_LEN), SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(name, SLE_ERR_INVALID_PARAM);
    if(g_sleHostState != SLE_HOST_STATE_ENABLED) {
        SLE_LOGW("sle is not enabled");
    }
    ret = SleHostHalSetHostName(name, len);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    memcpy_s(g_hostName, SLE_DEVICE_NAME_LEN, name, len);
    return ret;
}

SleErrorCode GetHostName(uint8_t *name, uint8_t *len)
{
    SLE_LOGD("GetHostName In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(name, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(len, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(*len <= SLE_DEVICE_NAME_LEN, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = SLE_ERR_FAILED;
    ret = SleHostHalGetHostName(name, len);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    memcpy_s(g_hostName, SLE_DEVICE_NAME_LEN, name, len);
    return ret;
}

SleErrorCode SleConvertByteToSleConnectConfig(uint8_t *data, SleConnectConfig *hostConnParam)
{
    SLE_ASSERT_AND_RETURN_RET(data != NULL, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(hostConnParam != NULL, SLE_ERR_INVALID_PARAM);

    uint8_t pos = 0;
    hostConnParam->connectInterval = COMBINE_BYTS_16(*(data + pos), *(data + pos + 1));
    pos += sizeof(uint16_t);
    hostConnParam->connectTimeout = COMBINE_BYTS_16(*(data + pos), *(data + pos + 1));
    pos += sizeof(uint16_t);
    hostConnParam->connectLatency = COMBINE_BYTS_16(*(data + pos), *(data + pos + 1));
    pos += sizeof(uint16_t);
    hostConnParam->mtuSize = COMBINE_BYTS_32(*(data + pos), *(data + pos + 1), *(data + pos + 2), *(data + pos + 3));
    pos += sizeof(uint32_t);
    hostConnParam->mcs = *(data + pos);
    pos += sizeof(uint8_t);
    hostConnParam->pilotDensity = *(data + pos);
    pos += sizeof(uint8_t);
    hostConnParam->formatType = *(data + pos);
    pos += sizeof(uint8_t);
    hostConnParam->dataLen = COMBINE_BYTS_16(*(data + pos), *(data + pos + 1));
    pos += sizeof(uint16_t);
    hostConnParam->phy = *(data + pos);
    pos += sizeof(uint8_t);

    return SLE_NO_ERROR;
}

SleErrorCode GetHostDefaultConnectConfig(SleConnectConfig *hostConnParam)
{
    SLE_LOGD("GetHostDefaultConnectConfig In");
    SLE_ASSERT_AND_RETURN_RET(hostConnParam != NULL, SLE_ERR_INVALID_PARAM);
    // read mode
    uint16_t realLen = 0;
    uint8_t data[SLE_NV_CONNECT_CONFIG_MODE_DATA_LEN] = {0};
    SleErrorCode ret = SleUapiNvRead(SLE_NV_CONNECT_ID, SLE_NV_CONNECT_ID_DATA_LEN, &realLen, data);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    SleConnectModeConfig connectMode;
    connectMode.configMode = data[SLE_NV_WORK_MODE_POS];
    memset(data, 0, SLE_NV_CONNECT_CONFIG_MODE_DATA_LEN);
    uint16_t keyId = 0;
    if(connectMode.configMode == SLE_NV_WORK_MODE_NORMAL_SPEED) {
        keyId = SLE_NV_CONNECT_CONFIG_MODE0_ID;
    }
    else {
        keyId = SLE_NV_CONNECT_CONFIG_MODE1_ID;
    }
        
    ret = SleUapiNvRead(keyId, SLE_NV_CONNECT_CONFIG_MODE_DATA_LEN, &realLen, data);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);

    return SleConvertByteToSleConnectConfig(data, hostConnParam);
}

static bool CheckIsRegistered(const SleHostCallbacks *hostCallback)
{
    for(uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_hostCallbacks[i] == hostCallback) {
            return true;
        }
    }
    return false;
}

SleErrorCode RegisterHostCallbacks(SleHostCallbacks *hostCallback)
{
    SLE_LOGD("RegisterHostCallbacks In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(hostCallback, SLE_ERR_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET_LOG(CheckIsRegistered(hostCallback) != true, SLE_NO_ERROR, "hostCallback(%p) is already registered", hostCallback);
    SleErrorCode ret = SLE_ERR_NO_MEM;
    osMutexAcquire(g_hostCallbcakMuxHandle, 0);
    for(uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_hostCallbacks[i] == NULL) {
            g_hostCallbacks[i] = hostCallback;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    osMutexRelease(g_hostCallbcakMuxHandle);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register host callback failed(%d)!", ret);
    return ret;
}

SleErrorCode UnregisterHostCallbacks(SleHostCallbacks *hostCallback)
{
    SLE_LOGD("UnregisterHostCallbacks In");
    SLE_ASSERT_AND_RETURN_RET(g_isSleHostServiceInit == true, SLE_ERR_NOT_READY);
    SLE_ASSERT_AND_RETURN_RET(hostCallback, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = SLE_ERR_FAILED;
    osMutexAcquire(g_hostCallbcakMuxHandle, 0);
    for(uint8_t i = 0; i < serviceCallbackNum; i++) {
        if (g_hostCallbacks[i] == hostCallback) {
            g_hostCallbacks[i] = NULL;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    osMutexRelease(g_hostCallbcakMuxHandle);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_NO_ERROR, "callback(%p) has not been registered", hostCallback);
    return ret;
}

SleErrorCode SleUapiNvRead(uint16_t key, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue)
{
    SLE_LOGD("SleUapiNvRead In");
    SLE_ASSERT_AND_RETURN_RET_LOG(kvalue_max_length > 0, SLE_ERR_INVALID_PARAM, "param error!,"
        "kvalue_max_length=%{public}d.", kvalue_max_length);

    SleErrorCode ret = SLE_ERR_FAILED;
    ret = SleHostHalUapiNvRead(key, kvalue_max_length, kvalue_length, kvalue);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return ret;
}

SleErrorCode SleUapiNvWrite(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length)
{
    SLE_LOGD("SleUapiNvWrite In");
    SLE_ASSERT_AND_RETURN_RET_LOG(kvalue != NULL, SLE_ERR_INVALID_PARAM, "param null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(kvalue_length > 0, SLE_ERR_INVALID_PARAM, "param error!,"
        "kvalue_length=%{public}d.", kvalue_length);

    SleErrorCode ret = SLE_ERR_FAILED;
    ret = SleHostHalUapiNvWrite(key, kvalue, kvalue_length);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return ret;
}